#include "logout.hh"
#include <iostream>
#include <thread>
#include <string.h>
#include <errno.h>
#include <strings.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <mutex>
#include <netinet/in.h>
#include <unistd.h>

#define buffer_size 1024

using namespace std;
class tcp_client_t
{
public:
    tcp_client_t(string addr, uint16_t port)
        : _is_res_printed(true)
    {
        pthread_mutex_init(&_print_lock,nullptr);
        pthread_cond_init(&_print_cond,nullptr);
        // info
        bzero(&_server_addr, sizeof(_server_addr));
        _server_addr.sin_family = AF_INET;
        _server_addr.sin_port = htons(port);
        _server_addr.sin_addr.s_addr = inet_addr(addr.c_str());
        // socket
        _connect_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
        if (_connect_socket_fd < 0)
        {
            log_error("creat_connect_socket_error!", SOCKET_CREAT_ERROR);
        }
    }
    ~tcp_client_t()
    {
        pthread_mutex_destroy(&_print_lock);
        pthread_cond_destroy(&_print_cond);
        close(_connect_socket_fd);
    }

public:
    void connect_to_server()
    {
        if (connect(_connect_socket_fd, (const sockaddr *)&_server_addr, sizeof(_server_addr)) < 0)
        {
            log_error("connect_to_server_error!", CONNECT_TO_SERVER_ERROR);
        }
    }
    void interact()
    {
        thread_argc_struct_t *thread_argc = new thread_argc_struct_t{_connect_socket_fd, this};
        thread sender{sender_thread_routine, thread_argc};
        thread receiver{receiver_thread_routine, thread_argc};
        sender.join();
        receiver.join();
        delete thread_argc;
    }

private:
    struct thread_argc_struct_t
    {
        thread_argc_struct_t(int fd, tcp_client_t *pthis)
            : _fd(fd), _this(pthis) { ; }
        int _fd;
        tcp_client_t *_this;
    };

    static void sender_thread_routine(thread_argc_struct_t *argc)
    {
        string buffer;
        volatile bool is_exit = false;
        while (true)
        {
            buffer.clear();
            pthread_mutex_lock(&argc->_this->_print_lock);
            while (!argc->_this->_is_res_printed)
            {
                cout<<"wait"<<endl;
                pthread_cond_wait(&argc->_this->_print_cond, &argc->_this->_print_lock);
                cout<<"aweek"<<endl;
            }
            cout << "please enter:> ";
            cout.flush();
            pthread_mutex_unlock(&argc->_this->_print_lock);
            getline(cin, buffer);
            if (buffer != "")
            {
                //不带'\n'
                ssize_t write_size = write(argc->_fd, buffer.c_str(), buffer.size());
                argc->_this->_is_res_printed=false;
            }
            if (strcasecmp(buffer.c_str(), "exit") == 0)
                break;
        }
        cout<<"exit"<<endl;
    }
    static void receiver_thread_routine(thread_argc_struct_t *argc)
    {
        char buffer[buffer_size];
        while (true)
        {
            bzero(buffer, buffer_size);
            //带'\n'
            ssize_t read_size = read(argc->_fd, buffer, buffer_size);
            if (read_size == 0)
            {
                break;
            }
            pthread_mutex_lock(&argc->_this->_print_lock);
            cout << buffer;
            cout.flush();
            argc->_this->_is_res_printed=true;
            pthread_cond_signal(&argc->_this->_print_cond);
            pthread_mutex_unlock(&argc->_this->_print_lock);
        }
    }
    sockaddr_in _server_addr;
    bool _is_res_printed;
    pthread_mutex_t _print_lock;
    pthread_cond_t _print_cond;
    int _connect_socket_fd;
};